#include "main.h"

char* types[] = {"","A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR",
		"NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT","","","","","","","","","","","","AAAA"};
void die(char* message){
	printf("\n%s\n", message);
	printf("Program will now terminate.\n");
	exit(1);
}
void* mycalloc(int n, int size){
	void * t;
	if ( (t=calloc(n,size))!=0)
		return t;
	else{
		die("Memorie insuficienta");
		return 0;
	}
}
struct Packet* newQuery(u16 id, int opcode, char* domain, u16 type, u16 clasa){
	struct Header * header = (struct Header*)mycalloc(1, sizeof(struct Header));
	header->id = id;
	header->qdcount = htons(1);
	header->options = 0x0100;
	u16 op = opcode;
	op = op<<11;
	header->options = (header->options | op);
	u16 rd = 1;
	rd = rd<<8;
	header->options = (header->options | rd);
	header->options = htons(header->options);
	struct Packet* pack = (struct Packet*)mycalloc(1, sizeof(struct Packet));
	pack->header = header;
	pack->question = (struct Question*)mycalloc(1, sizeof(struct Question));
	pack->question->qname = (char*)mycalloc(256, sizeof(char));	
	pack->question->qtype = htons(type);
	pack->question->qclass = htons(clasa);
	char* r = NULL;
	int k = 0;
	char* temp = (char*)mycalloc(strlen(domain), sizeof(char));
	memcpy(temp, domain, strlen(domain));
	r = strtok(temp, ".");
	while (r!= NULL){
		char l = strlen(r);
		pack->question->qname[k] = l;
		k++;
		memcpy(pack->question->qname + k, r, l);
		k+=l;
		r = strtok(NULL,"."); 
	} 
	return pack;
}
void sendPacket(struct Packet* pack, int sd, struct sockaddr_in* remoteServAddr){
	int total_length = sizeof(struct Header) + 4 + strlen(pack->question->qname)+1;
	char* data = (char*)mycalloc(total_length,sizeof(char));
	int k = 0;	
	memcpy(data+k, &pack->header->id, 2);
	k+=2;
	memcpy(data+k, &pack->header->options, 2);
	k+=2;
	memcpy(data+k, &pack->header->qdcount, 2);
	k+= 2;
	memcpy(data+k, &pack->header->ancount, 2);
	k+= 2;
	memcpy(data+k, &pack->header->nscount, 2);
	k+= 2;
	memcpy(data+k, &pack->header->arcount, 2);
	k+= 2;
	memcpy(data+k, pack->question->qname, strlen(pack->question->qname) + 1);
	k+= strlen(pack->question->qname) + 1;
	*(u16*)(data+k) = pack->question->qtype;
	k+=2;
	*(u16*)(data+k) = pack->question->qclass;
	sendto(sd, data, total_length, 0, 
		(struct sockaddr *) remoteServAddr, 
		sizeof(struct sockaddr_in));
}
void printResponse(struct Packet* pack, char* domain){
	if (*was_timeout)
		return;	
	printf(";<<>> dnsClient 0.0.0 <<>> %s\n", domain);
	printf(";; Got answer:\n");
	int opcode = ((pack->header->options)&0x7800)>>11;
	char* opc, *sqr, *saa, *stc, *srd, *sra, *serr;	
	if (opcode == 0)
		opc = "QUERY";
	else
		opc = "INVERSE QUERY";
	int rcode = (pack->header->options & 0x000F);
	if (rcode == 0)
		serr = "NO ERROR";
	else
		serr = "ERROR";
	printf(";; ->>HEADER<<- opcode:%s status: %d id: %d\n",opc,rcode, pack->header->id);
	int qr = ((pack->header->options)&0x8000)>>15;
	if (qr == 1)
		sqr = "qr ";
	else
		sqr = "";
	int aa = ((pack->header->options)&0x0400)>>10;
	if (aa == 1)
		saa = "aa ";
	else
		saa = "";
	int tc = ((pack->header->options)&0x0200)>>9;
	if (tc == 1)
		stc = "tc ";
	else
		stc = "";
	int rd = ((pack->header->options)&0x0100)>>8;
	if (rd == 1)
		srd ="rd ";
	else
		srd = "";
	int ra = ((pack->header->options)&0x0080)>>7;
	if (ra == 1)
		sra ="ra ";
	else
		sra = "";
	printf(";; flags: %s%s%s%s%s QUERY: %d ANSWER: %d AUTHORITY: %d ADDITIONAL: %d\n", sqr,saa,stc, srd,sra,
		pack->header->qdcount, pack->header->ancount,
		pack->header->nscount, pack->header->arcount);	
	printf("\n;; QUESTION SECTION:\n");
	int i;
	for (i=0;i<pack->header->qdcount; i++){
		printf("%s \t\t\tIN\t%s\n", pack->question[i].qname, types[pack->question[i].qtype]);
	}
	printf("\n;; ANSWER SECTION:\n");
	for (i=0;i<pack->header->ancount;i++){
		printf("%s \t\t%d\tIN\t%s\t%s\n", 
			pack->answer[i].name, 
			pack->answer[i].ttl, 
			types[pack->answer[i].type], 
			value(pack->answer+i, pack));
	}
	printf("\n;; AUTHORITY SECTION: \n");
	for (i=0;i<pack->header->nscount;i++)
		printf("%s \t\t%d\tIN\t%s\t%s\n", 
			pack->authority[i].name, 
			pack->authority[i].ttl, 
			types[pack->authority[i].type], 
			value(pack->authority+i, pack));
	
	printf("\n;; ADDITIONAL SECTION: \n");
	for (i=0;i<pack->header->arcount;i++)
		printf("%s \t\t%d\tIN\t%s\t%s\n", 
			pack->additional[i].name, 
			pack->additional[i].ttl, 
			types[pack->additional[i].type], 
			value(pack->additional+i, pack));
	
}
char* value(struct Answer* ans, struct Packet* pack){
	char * val = (char*)mycalloc(500, sizeof(char));
	switch (ans->type){
		case 1:{ //A
			sprintf(val, "%d.%d.%d.%d", 
				(unsigned char)ans->rdata[0], 
				(unsigned char)ans->rdata[1], 
				(unsigned char)ans->rdata[2], 
				(unsigned char)ans->rdata[3]);
			break;
		}
		case 5:{
			int f,j,i = 0, k = 0;
			while (ans->rdata[i]!=0){
				u16 pointer = htons(*(u16*)(ans->rdata + i));
				if (pointer >> 14 == 3){
					pointer = pointer & 0x3FFF;
					char* last = decompress(pack->message, pointer, &f);
					memcpy(val+k, last, strlen(last));
					k+= strlen(last);
					break;
				}
				for (j=1;j<=ans->rdata[i];j++)
					val[k++] = ans->rdata[i+j];
				i+=ans->rdata[i]+1;
				val[k++] = '.';
			}
			break;
		}
		case 2:{
			int f,j,i = 0, k = 0;
			while (ans->rdata[i]!=0){
				u16 pointer = htons(*(u16*)(ans->rdata + i));
				if (pointer >> 14 == 3){
					pointer = pointer & 0x3FFF;
					char* last = decompress(pack->message, pointer, &f);
					memcpy(val+k, last, strlen(last));
					k+= strlen(last);
					break;
				}
				for (j=1;j<=ans->rdata[i];j++)
					val[k++] = ans->rdata[i+j];
				i+=ans->rdata[i]+1;
				val[k++] = '.';
			}
			break;
		}
		case 12:{
			int f,j,i = 0, k = 0;
			while (ans->rdata[i]!=0){
				u16 pointer = htons(*(u16*)(ans->rdata + i));
				if (pointer >> 14 == 3){
					pointer = pointer & 0x3FFF;
					char* last = decompress(pack->message, pointer, &f);
					memcpy(val+k, last, strlen(last));
					k+= strlen(last);
					break;
				}
				for (j=1;j<=ans->rdata[i];j++)
					val[k++] = ans->rdata[i+j];
				i+=ans->rdata[i]+1;
				val[k++] = '.';
			}
			break;
		}
		case 6:{ //SOA
			int f,j,i = 0, k = 0;
			while (ans->rdata[i]!=0){
				u16 pointer = htons(*(u16*)(ans->rdata + i));
				if (pointer >> 14 == 3){
					pointer = pointer & 0x3FFF;
					char* last = decompress(pack->message, pointer, &f);
					memcpy(val+k, last, strlen(last));
					k+= strlen(last);
					i++;
					break;
				}
				for (j=1;j<=ans->rdata[i];j++)
					val[k++] = ans->rdata[i+j];
				i+=ans->rdata[i]+1;
				val[k++] = '.';
			}
			i++;
			val[k++] = ' ';
			val[k++] = ' ';
			while (ans->rdata[i]!=0){
				u16 pointer = htons(*(u16*)(ans->rdata + i));
				if (pointer >> 14 == 3){
					pointer = pointer & 0x3FFF;
					char* last = decompress(pack->message, pointer, &f);
					memcpy(val+k, last, strlen(last));
					k+= strlen(last);
					i+=1;
					break;
				}
				for (j=1;j<=ans->rdata[i];j++)
					val[k++] = ans->rdata[i+j];
				i+=ans->rdata[i]+1;
				val[k++] = '.';
			}
			i+=3;
			val[k++] = ' ';
			sprintf(val + k, "%d ",htons(*(int*)(ans->rdata+i)));
			k = strlen(val);
			i+=4;
			sprintf(val+ k, "%d ", htons(*(int*)(ans->rdata+i)));
			k = strlen(val);
			i+=4;
			sprintf(val+k, "%d ", htons(*(unsigned int*)(ans->rdata + i)));
			k = strlen(val);
			i+=4;
			sprintf(val+k, "%d ", htons(*(unsigned int*)(ans->rdata + i)));
			k = strlen(val);
			i+=4;
			sprintf(val+k, "%d ", htons(*(unsigned int*)(ans->rdata + i)));
			k = strlen(val);
			i+=4;
			break;
		}
		case 28:{
			int i;
			for (i=0;i<strlen(ans->rdata);i+=2)
				sprintf(val+strlen(val), "%x ", (ans->rdata[i]<<8)+ans->rdata[i+1]);
			break;
		}
		case 15:{
			int k = 0;
			u16 preference = ntohs(*(u16*)(ans->rdata + k));
			sprintf(val, "%d ",preference);
			k = strlen(val);
			int f,j;
			int i = 2;
			while (ans->rdata[i]!=0){
				u16 pointer = htons(*(u16*)(ans->rdata + i));
				if (pointer >> 14 == 3){
					pointer = pointer & 0x3FFF;
					char* last = decompress(pack->message, pointer, &f);
					memcpy(val+k, last, strlen(last));
					k+= strlen(last);
					i++;
					break;
				}
				for (j=1;j<=ans->rdata[i];j++)
					val[k++] = ans->rdata[i+j];
				i+=ans->rdata[i]+1;
				val[k++] = '.';
			} 
			break;
		}
		default:{
			sprintf(val, "Unknown Packet type: %d",ans->type );
			break;
		}
	}
	return val;
}
struct Packet* receivePacket(int sd){
	char buff[4096];
	int i;
	unsigned int n = 0;
	struct sockaddr_in serv;
	unsigned int servlen = sizeof(serv);
	n = recvfrom(sd, buff, 4096, 0, (struct sockaddr *)&serv, &servlen);
	int k = 0;
	struct Header * header = (struct Header*)mycalloc(1, sizeof(struct Header));
	header->id = *(u16*)(buff+k);
	k+=2;
	header->options = htons(*(u16*)(buff+k));
	k+=2;
	header->qdcount = htons(*(u16*)(buff+k));
	k+=2;
	header->ancount = htons(*(u16*)(buff+k));
	k+=2;
	header->nscount = htons(*(u16*)(buff+k));
	k+=2;
	header->arcount = htons(*(u16*)(buff+k));
	k+=2;
	struct Packet* pack = (struct Packet*)mycalloc(1, sizeof(struct Packet));
	pack->header = header;
	pack->question = (struct Question*)mycalloc(header->qdcount, sizeof(struct Question));
	u16 pointer;

	/*
		Prelucrare sectiunea questions
	*/	
	int jumped,j,t,last, kq;
	struct Question *q;
	for (i=0;i<header->qdcount;i++){
		q = pack->question+i;
		q->qname = (char*)mycalloc(256, sizeof(char));
		kq = 0; //index qname
		last = 0;
		jumped = 0;
		j = k;
		while (buff[j]!=0){
			pointer = htons(*(u16*)(buff+j));
			if (pointer>>14 == 3){ //daca e pointer
				pointer = pointer&0x3FFF;
				if (jumped == 0){ //in cazul salturilor multiple, memoreaza prima adresa de unde a sarit				
					last = j+2;
					jumped = 1;
				}
				j = pointer;
			}
			else{//e numar de caractere
				for (t=1;t<=buff[j];t++){
					q->qname[kq++] = buff[j+t];
				}
				q->qname[kq++] = '.';
				j+=buff[j]+1;
			}
		}
		if (last!=0)
			k = last;
		else
			k = j+1;
		q->qtype = htons(*(u16*)(buff+k));
		k+=2;
		q->qclass = htons(*(u16*)(buff+k));
		k+=2;
	
	}
	
	/*
		Prelucrare sectiunea answers
	*/	
	pack->answer = (struct Answer*)mycalloc(header->ancount, sizeof(struct Answer));
	struct Answer* a;
	int ka;
	for (i=0;i<header->ancount;i++){
		j = k;
		a = pack->answer+i;
		a->name = (char*)mycalloc(256, sizeof(char));
		last = 0;
		ka = 0;
		jumped = 0;
		while (buff[j]!=0){
			pointer = htons(*(u16*)(buff+j));
			if (pointer>>14==3){ //daca e pointer
				pointer = pointer&0x3FFF;
				if (jumped == 0){ //in cazul salturilor multiple, memoreaza prima adresa de unde a sarit				
					last = j+2;
					jumped = 1;
				}
				j = pointer;
			}
			else{//e numar de caractere
				for (t=1;t<=buff[j];t++){
					a->name[ka++] = buff[j+t];
				}
				j+=buff[j]+1;
				a->name[ka++] = '.';
			}
		}
		if (last!=0)
			k = last;
		else
			k = j+1;
		a->type = htons(*(u16*)(buff + k));
		k+=2;
		a->clasa = htons(*(u16*)(buff + k));
		k+=2;
		a->ttl = htons(*(int*)(buff + k));
		k+=4;
		a->rdlength = ntohs(*(u16*)(buff + k));
		k+=2;
		a->rdata = (char*)mycalloc(a->rdlength+63, sizeof(char));
		memcpy(a->rdata, buff+k, a->rdlength);
		k+=a->rdlength;
	}

	/*
		Prelucrare sectiunea authority
	*/	
	pack->authority = (struct Answer*)mycalloc(header->nscount, sizeof(struct Answer));
	struct Answer* au;
	for (i=0;i<header->nscount;i++){
		j = k;
		au = pack->authority+i;
		au->name = (char*)mycalloc(256, sizeof(char));
		last = 0;
		ka = 0;
		jumped = 0;
		while (buff[j]!=0){
			pointer = htons(*(u16*)(buff+j));
			if (pointer>>14==3){ //daca e pointer
				pointer = pointer&0x3FFF;
				if (jumped == 0){ //in cazul salturilor multiple, memoreaza prima adresa de unde a sarit				
					last = j+2;
					jumped = 1;
				}
				j = pointer;
			}
			else{//e numar de caractere
				for (t=1;t<=buff[j];t++){
					au->name[ka++] = buff[j+t];
				}
				j+=buff[j]+1;
				au->name[ka++] = '.';
			}
		}
		if (last!=0)
			k = last;
		else
			k = j+1;
		au->type = htons(*(u16*)(buff + k));
		k+=2;
		au->clasa = htons(*(u16*)(buff + k));
		k+=2;
		au->ttl = htons(*(int*)(buff + k));
		k+=4;
		au->rdlength = ntohs(*(u16*)(buff + k));
		k+=2;
		au->rdata = (char*)mycalloc(au->rdlength+2, sizeof(char));
		memcpy(au->rdata, buff+k, au->rdlength);
		k+=au->rdlength;
	}

	/*
		Prelucrare sectiunea additional
	*/	
	pack->additional = (struct Answer*)mycalloc(header->arcount, sizeof(struct Answer));
	struct Answer* ad;
	for (i=0;i<header->arcount;i++){
		j = k;
		ad = pack->additional+i;
		ad->name = (char*)mycalloc(256, sizeof(char));
		last = 0;
		ka = 0;
		jumped = 0;
		while (buff[j]!=0){
			pointer = htons(*(u16*)(buff+j));
			if (pointer>>14==3){ //daca e pointer
				pointer = pointer&0x3FFF;
				if (jumped == 0){ //in cazul salturilor multiple, memoreaza prima adresa de unde a sarit				
					last = j+2;
					jumped = 1;
				}
				j = pointer;
			}
			else{//e numar de caractere
				for (t=1;t<=buff[j];t++){
					ad->name[ka++] = buff[j+t];
				}
				j+=buff[j]+1;
				ad->name[ka++] = '.';
			}
		}
		if (last!=0)
			k = last;
		else
			k = j+1;
		ad->type = htons(*(u16*)(buff + k));
		k+=2;
		ad->clasa = htons(*(u16*)(buff + k));
		k+=2;
		ad->ttl = htons(*(int*)(buff + k));
		k+=4;
		ad->rdlength = ntohs(*(u16*)(buff + k));
		k+=2;
		ad->rdata = (char*)mycalloc(ad->rdlength+2, sizeof(char));
		memcpy(ad->rdata, buff+k, ad->rdlength);
		k+=ad->rdlength;
	}
	pack->message = buff;
	return pack;
}
char *decompress(char* buff, int i, int *f){
	int t,j = i;
	u16 pointer;
	int jumped = 0;
	int last = 0;
	int kq = 0;
	char* name = (char*)mycalloc(300, sizeof(char));	
	while (buff[j]!=0){
		pointer = htons(*(u16*)(buff+j));
		if (pointer>>14 == 3){ //daca e pointer
			pointer = pointer&0x3FFF;
			if (jumped == 0){ //in cazul salturilor multiple, memoreaza prima adresa de unde a sarit				
				last = j+2;
				jumped = 1;
			}
			j = pointer;
		}
		else{//e numar de caractere
			for (t=1;t<=buff[j];t++){
				name[kq++] = buff[j+t];
			}
			name[kq++] = '.';
			j+=buff[j]+1;
		}
	}
	if (last!=0)
		*f = last;
	else
		*f = j+1;
	return name;
}
void conectare(char* ipadr, int* send_sock, int* receive_sock, struct sockaddr_in* remoteServAddr){
	int sd, rc;
	struct sockaddr_in cliAddr;

  /* completarea structurii sockaddr_in pentru server, necesara la sendto() */

	remoteServAddr->sin_family = AF_INET;
	inet_aton(ipadr, &remoteServAddr->sin_addr);
  	remoteServAddr->sin_port = htons(UDP_PORT);
	sd = socket(AF_INET,SOCK_DGRAM,0);	// socket de tip SOCK_DGRAM pt UDP
	if(sd<0) {
	printf("cannot open socket \n");
	exit(1);
	}
	cliAddr.sin_family = AF_INET;
	cliAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	cliAddr.sin_port = htons(0); // alegere automata a unui port disponibil  
	rc = bind(sd, (struct sockaddr *) &cliAddr, sizeof(cliAddr));
	if(rc<0) {
		printf("cannot bind port\n");
		exit(1);
	}
	*send_sock = sd;
	*receive_sock = sd;
}
char *strrev(char* str){
	int l, i;
	char* reversed = (char*)mycalloc(strlen(str), sizeof(char));
	l = strlen(str);
	for (i=1;i<=l;i++)
		reversed[i-1] = str[l-i];
	return reversed;
}
